home *** CD-ROM | disk | FTP | other *** search
Lex Description | 1992-06-10 | 12.9 KB | 587 lines |
- %{
- /*
- * this file implements the grammar for the password tests
- * the escapes are handled in the caller
- */
-
- #include "passwd.h"
-
- %}
-
- /*
- * the lexer returns both numbers and strings
- */
- %union {
- char *cval; /* something parsed as a string */
- int ival; /* something parsed as a number */
- }
-
- /*
- * tokens returned by the lexical analyzer yylex()
- */
- %token <ival> AND /* number: (logical) disjunction */
- %token <ival> DIV /* number: (arithmetic) division */
- %token <ival> EOL /* number: no more input */
- %token <ival> EQ /* number: (relational) equals */
- %token <cval> FILENAME /* string: a file */
- %token <ival> GE /* number: (relational) greater than or equal */
- %token <ival> GT /* number: (relational) greater than */
- %token <ival> LE /* number: (relational) less than or equal */
- %token <ival> LPAR /* number: begin grouping */
- %token <ival> LT /* number: (relational) less than */
- %token <ival> MINUS /* number: (arithmetic) subtraction, negation */
- %token <ival> MOD /* number: (arithmetic) remaindering */
- %token <ival> NE /* number: (relational) not equal */
- %token <ival> NOT /* number: (logical) negation */
- %token <ival> NUMBER /* number: number or value of a variable */
- %token <ival> OR /* number: (logical) conjunction */
- %token <ival> PATEQ /* number: (pattern) match */
- %token <ival> PATNE /* number: (pattern) no match */
- %token <cval> STRING /* string: compare to a pattern */
- %token <ival> PLUS /* number: (arithmetic) addition */
- %token <cval> PROGRAM /* string: a program */
- %token <ival> RPAR /* number: end grouping */
- %token <ival> TIMES /* number: (arithmetic) multiplication */
- %token <ival> UNK /* number: unknown token */
-
- /*
- * productions analyzed by the parser
- */
- %type <ival> number /* number: something arithmetic */
- %type <ival> prim /* number: 1 if relation is satisfied */
- %type <ival> stat /* number: test with EOL tacked on */
- %type <ival> string /* number: 1 if string relation true */
- %type <ival> test /* number: 1 if test passed so far */
-
- /*
- * expression operators
- * these must be on the same line since they are of equal precedence
- */
- %right NOT /* negation of tests */
- %left OR AND /* grouping of tests */
- %left TIMES DIV MOD /* usual arithmetic ordering */
- %left PLUS MINUS
- %nonassoc LT GT NE EQ GE LE /* relational operators don't associate */
-
- %{
-
- /*
- * variables
- */
- static int retval; /* 1 if test passed, 0 if not */
- static char *lptr; /* used to walk input string */
- static int ateol; /* 1 when you hit end of string */
-
- %}
-
- /*
- * start analysis at state stat
- */
- %start stat
-
- %%
- stat : test EOL
- { retval = $1 ; }
- | error EOL
- { retval = 1; }
- | EOL
- { retval = 0; }
- ;
-
- test : LPAR test RPAR
- { $$ = $2 ; }
- | test AND test
- { $$ = $1 && $3 ; }
- | test OR test
- { $$ = $1 || $3 ; }
- | NOT test
- { $$ = ! $2 ; }
- | string
- { $$ = $1 ; }
- | prim
- { $$ = $1 ; }
- ;
-
- string : STRING EQ STRING
- { $$ = (strcmp( $1 , $3 ) == 0);
- (void) free( $1 ); (void) free( $3 );
- }
- | STRING NE STRING
- { $$ = (strcmp( $1 , $3 ) != 0);
- (void) free( $1 ); (void) free( $3 );
- }
- | STRING PATEQ STRING
- { if (smatch( $3 )) YYERROR; $$ = match( $1 );
- (void) free( $1 ); (void) free( $3 );
- }
- | STRING PATNE STRING
- { if (smatch( $3 )) YYERROR; $$ = !match( $1 );
- (void) free( $1 ); (void) free( $3 );
- }
- | STRING EQ FILENAME
- { $$ = strfp(1, $1 , $3 , fopen, fclose);
- (void) free( $1 ); (void) free( $3 );
- }
- | STRING NE FILENAME
- { $$ = strfp(0, $1 , $3 , fopen, fclose);
- (void) free( $1 ); (void) free( $3 );
- }
- | STRING PATEQ FILENAME
- { $$ = patinfp(1, $1 , $3 , fopen, fclose);
- (void) free( $1 ); (void) free( $3 );
- }
- | STRING PATNE FILENAME
- { $$ = patinfp(0, $1 , $3 , fopen, fclose);
- (void) free( $1 ); (void) free( $3 );
- }
- | STRING EQ PROGRAM
- { $$ = strfp(1, $1 , $3 , popen, pclose);
- (void) free( $1 ); (void) free( $3 );
- }
- | STRING NE PROGRAM
- { $$ = strfp(0, $1 , $3 , popen, pclose);
- (void) free( $1 ); (void) free( $3 );
- }
- | STRING PATEQ PROGRAM
- { $$ = patinfp(1, $1 , $3 , popen, pclose);
- (void) free( $1 ); (void) free( $3 );
- }
- | STRING PATNE PROGRAM
- { $$ = patinfp(0, $1 , $3 , popen, pclose);
- (void) free( $1 ); (void) free( $3 );
- }
- | FILENAME EQ STRING
- { $$ = strfp(1, $3 , $1 , fopen, fclose);
- (void) free( $1 ); (void) free( $3 );
- }
- | FILENAME NE STRING
- { $$ = strfp(0, $3 , $1 , fopen, fclose);
- (void) free( $1 ); (void) free( $3 );
- }
- | FILENAME PATEQ STRING
- { $$ = patfp(1, $3 , $1 , fopen, fclose);
- (void) free( $1 ); (void) free( $3 );
- }
- | FILENAME PATNE STRING
- { $$ = patfp(0, $3 , $1 , fopen, fclose);
- (void) free( $1 ); (void) free( $3 );
- }
- | PROGRAM EQ STRING
- { $$ = strfp(1, $3 , $1 , popen, pclose);
- (void) free( $1 ); (void) free( $3 );
- }
- | PROGRAM NE STRING
- { $$ = strfp(0, $3 , $1 , popen, pclose);
- (void) free( $1 ); (void) free( $3 );
- }
- | PROGRAM PATEQ STRING
- { $$ = patfp(1, $3 , $1 , popen, pclose);
- (void) free( $1 ); (void) free( $3 );
- }
- | PROGRAM PATNE STRING
- { $$ = patfp(0, $3 , $1 , popen, pclose);
- (void) free( $1 ); (void) free( $3 );
- }
- ;
-
- prim : number LT number
- { $$ = $1 < $3 ; }
- | number GT number
- { $$ = $1 > $3 ; }
- | number NE number
- { $$ = $1 != $3 ; }
- | number EQ number
- { $$ = $1 == $3 ; }
- | number GE number
- { $$ = $1 >= $3 ; }
- | number LE number
- { $$ = $1 <= $3 ; }
- ;
-
- number : LPAR number RPAR
- { $$ = $2 ; }
- | number PLUS number
- { $$ = $1 + $3 ; }
- | PLUS number %prec TIMES
- { $$ = $1 ; }
- | number MINUS number
- { $$ = $1 - $3 ; }
- | MINUS number %prec TIMES
- { $$ = - $1 ; }
- | number TIMES number
- { $$ = $1 * $3 ; }
- | number DIV number
- { $$ = $1 / $3 ; }
- | number MOD number
- { $$ = $1 % $3 ; }
- | NUMBER
- { $$ = $1 ; }
- ;
-
- %%
-
- /*
- * this is the lexer -- it's pretty dumb
- */
- yylex()
- {
- static char parbuf[BUFSIZ]; /* used to save strings */
- register int rval; /* used for return values */
- register char quo; /* used to hold terminal quote mark */
-
- /*
- * this is hit at the end of string
- * we need to do it this way because the '\0' (EOL)
- * token must be returned, so we have toi return
- * another "end of input" token -- in other words,
- * the end of input character is NOT the same as
- * the end of string (EOL) character
-
- */
- if (ateol){
- ateol = 0;
- return(-1); /* YACC's end of file character */
- }
-
- /*
- * eat leading white spaces; may have a backslash in front
- * (since a tab separates the test field and the message
- * field, if there is a tab in the test field it must be
- * escaped)
- */
- while(*lptr)
- if (isspace(*lptr))
- lptr++;
- else if (*lptr == '\\'){
- if (isspace(lptr[1]))
- lptr += 2;
- else
- break;
- }
- else
- break;
-
- /*
- * hit end of string character
- * indicate there's nothing more with ateol
- * (so next tile we return YACC's end of file)
- * and return the EOL token
- */
- if (*lptr == '\0'){
- ateol = 1;
- return(EOL);
- }
-
- /*
- * number -- just return it
- */
- if (isdigit(*lptr)){
- for(rval = 0; isdigit(*lptr); lptr++)
- rval = rval * 10 + *lptr - '0';
- yylval.ival = rval;
- return(NUMBER);
- }
-
- /*
- * something else -- analyze it
- */
- switch(*lptr++){
- case '@': /* rest of line is comment */
- case '\t': /* rest of line is error message */
- case '\n': /* end of line */
- case '\0': /* end of line */
- ateol = 1;
- return(EOL);
- case '(': /* begin grouping */
- return(LPAR);
- case ')': /* end grouping */
- return(RPAR);
- case '~': /* negation */
- return(NOT);
- case '+': /* add */
- return(PLUS);
- case '-': /* subtract */
- return(MINUS);
- case '*': /* multiply */
- return(TIMES);
- case '/': /* divide */
- return(DIV);
- case '%': /* remainder */
- return(MOD);
- case '&': /* disjunction */
- if (*lptr++ == '&') /* && */
- return(AND);
- --lptr; /* & */
- return(AND);
- case '|': /* conjunction */
- if (*lptr++ == '|') /* || */
- return(OR);
- --lptr; /* | */
- return(OR);
- case '>': /* relation */
- if (*lptr++ == '=') /* >= */
- return(GE);
- --lptr; /* > */
- return(GT);
- case '<': /* relation */
- if (*lptr++ == '=') /* <= */
- return(LE);
- --lptr; /* < */
- return(LT);
- case '!': /* relation, logical negation */
- if (*lptr++ == '=') /* != */
- return(NE);
- --lptr;
- if (*lptr++ == '~') /* !~ */
- return(PATNE);
- --lptr; /* logical negation */
- return(NOT);
- case '=':
- if (*lptr++ == '=') /* == */
- return(EQ);
- --lptr;
- if (*lptr++ == '~') /* =~ */
- return(PATEQ);
- --lptr; /* = */
- return(EQ);
- case '\'': /* pattern */
- case '"': /* pattern */
- case '{': /* program */
- case '[': /* file */
- /*
- * figure out what you're dealing with
- */
- if (lptr[-1] == '\'' || lptr[-1] == '"'){ /* pattern */
- quo = lptr[-1];
- rval = STRING;
- }
- else if (lptr[-1] == '{'){ /* program */
- quo = '}';
- rval = PROGRAM;
- }
- else{ /* file */
- quo = ']';
- rval = FILENAME;
- }
- /*
- * collect the file or program or pattern
- */
- lptr = getcstring(lptr, parbuf, quo);
- /*
- * if no closing quote, warn but accept;
- * if a closing quote, skip it
- */
- if (!*lptr)
- yyerror("missing quote -- supplying it");
- else
- lptr++;
- /*
- * return the file or program as the value
- */
- yylval.cval = strsave(parbuf);
- return(rval);
- }
-
- /*
- * unknown
- */
- return(UNK);
- }
-
- /*
- * report error on a pattern (malformed ...)
- */
- paterr(msg)
- char *msg; /* error message from pattern compiler/matcher */
- {
- /*
- * print the error message
- */
- LOG3(LG_SYNTAX, "%s at line %d (at \"%s\")", msg, linect, lptr-1);
- }
-
- /*
- * report system errors
- */
- sysyyerror(s)
- char *s; /* what screwed up */
- {
- char buf[BUFSIZ]; /* buffer for error message */
-
- /*
- * print the system error message if available,
- * or the error number if not
- */
- if (errno < sys_nerr)
- SPRINTF(buf, "line %d: %s: %s at \"%s\"",
- linect, s, sys_errlist[errno], lptr-1);
- else
- SPRINTF(buf, "line %d: %s: unknown error #%d at \"%s\"",
- linect, s, errno, lptr-1);
- LOG0(LG_SYSTEM, buf);
- }
-
- /*
- * report grammar errors (yacc)
- */
- yyerror(s)
- char *s; /* how the parse screwed up */
- {
- /*
- * print the error message
- */
- LOG3(LG_SYNTAX, "%s at line %d (at \"%s\")", s, linect, lptr-1);
-
- /*
- * signal end of test
- */
- ateol = 1;
- }
-
- /*==================== DRIVER ====================*/
- /*
- * this runs the test in "buf" on the password
- * and returns 1 if it passes, 0 of not
- */
- passtest(buf)
- char *buf; /* the test */
- {
- /*
- * clear the end of line flag
- */
- ateol = 0;
-
- /*
- * clobber any trailing newline
- */
- lptr = &buf[strlen(buf)-1];
- if (*lptr == '\n')
- *lptr = '\0';
- else
- lptr[2] = '\0';
-
- /*
- * set up the pointer to the input
- * for yylex, the lexical analyzer
- */
- lptr = buf;
-
- /*
- * parse the date and process the result
- */
- if (yyparse()){
- #ifdef DEBUG
- PRINTF("illegal test for password\n");
- #endif
- return(0);
- }
-
- /*
- * test is syntactically and semantically correct
- * and return the result
- */
- #ifdef DEBUG
- PRINTF("password \"%s\" %s this test\n",
- password, retval ? "passes" : "does not pass");
- #endif
- return(retval);
- }
-
-
-
-
- /*===================== M A I N C A L L I N G R O U T I N E S ==============*/
- /*
- * two sets of main routines
- * if "DEBUG" is defined, you get a main routine that reads in one date,
- * parses it, and prints the result
- * if "DEBUG" is not defined, you get a routine that returns 1 if the current
- * time is within the time range of the argument string, 0 if not
- *
- * "DEBUG" san be set to two levels; "1" gives you just the result (1 or 0),
- * but you can use the print function "prtime()" to print out key times in
- * the parse. "2" gives you complete debugging info from YACC as well
- */
-
- #ifdef DEBUG
-
- /*
- * set the flag YYDEBUG
- */
- #if DEBUG > 1
- #define YYDEBUG /* flag so YACC will generate debugging info */
- extern int yydebug; /* constant to tell YACC to generate debugging info */
- #endif
-
- int linect = 0; /* number of expression being tested */
- char *password; /* password being tested */
-
- /*
- * main routine -- execute the given test and print the result
- */
- main()
- {
- char buf[BUFSIZ]; /* input buffer */
- char pbuf[BUFSIZ]; /* buffer for password */
- register char *p, *b; /* used to load password */
-
- #if DEBUG > 1
- yydebug = 1; /* YACC is to give full debugging output */
- #endif
-
- /*
- * initialize the password to nothing
- */
- pbuf[0] = '\0';
-
- /*
- * get the input; if EOF, quit
- */
- while(fgets(buf, BUFSIZ, stdin) != CH_NULL){
- /*
- * if it is a new password change the old one
- */
- if (buf[0] == 'p'){
- for(b = &buf[1]; isspace(*b); b++)
- p = pbuf;
- while(*b && *b != '\n')
- *p++ = *b++;
- *p = '\0';
- PRINTF("new password is \"%s\"\n", pbuf);
- password = pbuf;
- initpw();
- continue;
- }
-
- /*
- * if there is no password, warn
- */
- if (pbuf[0] == '\0'){
- PRINTF("no password; say 'p <password>' to set one\n");
- continue;
- }
-
- /*
- * new expression
- */
- linect++;
-
- /*
- * print the buffer
- */
- PRINTF("buf is <%s>\n", buf);
-
- /*
- * parse the date and process the result
- */
- (void) passtest(buf);
- }
-
- /*
- * no problem
- */
- exit(0);
- }
-
- #endif
-